use crate::action::device::UpdateEnabledError;
use crate::data::message::Message;
use crate::fairing::device::DeviceDb;
use crate::guard::userauth::AdminUserData;
use crate::model::device::{DeviceId, DeviceIdNum};
use rocket::http::Status;
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
use rocket::route::Route;
use rocket::serde::json::Json;
use rocket_db_pools::sqlx;
use rocket_db_pools::{sqlx::Acquire, Connection};
use serde::Deserialize;

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
struct UpdateDeviceInfo {
    enabled: bool,
}

enum UpdateDeviceStatus {
    Ok,
    NoDeviceFound,
    DatabaseError(sqlx::Error),
}

impl<'r> Responder<'r, 'static> for UpdateDeviceStatus {
    fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
        match self {
            UpdateDeviceStatus::Ok => Response::build().status(Status::NoContent).ok(),
            UpdateDeviceStatus::NoDeviceFound => Response::build().status(Status::NotFound).ok(),
            UpdateDeviceStatus::DatabaseError(err) => Response::build_from(
                Json(Message {
                    message: err.to_string(),
                })
                .respond_to(req)?,
            )
            .status(Status::InternalServerError)
            .ok(),
        }
    }
}

#[patch("/<id>", data = "<device_info>")]
async fn update(
    mut db: Connection<DeviceDb>,
    _user: AdminUserData,
    id: DeviceIdNum,
    device_info: Json<UpdateDeviceInfo>,
) -> UpdateDeviceStatus {
    let result = async {
        let mut tx = db.begin().await?;

        let id = DeviceId(id);
        if let Err(err) = id.update_enabled(&mut tx, device_info.enabled).await {
            match err {
                UpdateEnabledError::Database(err) => return Err(err),
                UpdateEnabledError::NotFound => return Ok(UpdateDeviceStatus::NoDeviceFound),
            }
        }

        tx.commit().await?;
        Ok(UpdateDeviceStatus::Ok)
    }
    .await;
    match result {
        Ok(status) => status,
        Err(err) => UpdateDeviceStatus::DatabaseError(err),
    }
}

pub fn route() -> Vec<Route> {
    routes![update]
}
